package com.mini.mybatis.mybatisministep03.binding;

import cn.hutool.core.lang.ClassScanner;
import com.mini.mybatis.mybatisministep03.sqlsession.SqlSession;
import com.mini.mybatis.mybatisministep03.sqlsession.configuration.Configuration;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 *  MapperRegistry,负责存储注册mapper类模板和MapperProxyFactory的映射
 */
public class MapperRegistry {

    private Configuration configuration;

    private Map<Class<?>, MapperProxyFactory<?>> knowMappers = new HashMap<>();


    public MapperRegistry(Configuration configuration){
        this.configuration = configuration;
    }


    /**
     * 获取对应类型的MapperProxy
     * @param type
     * @param sqlSession
     * @return
     * @param <T>
     */
    public <T> T getMapper(Class<T> type, SqlSession sqlSession){
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knowMappers.get(type);
        if (mapperProxyFactory == null){
            throw new RuntimeException("type " + type + " is not known to the MapperRegistry");
        }
        try{
            return mapperProxyFactory.newInstance(sqlSession);
        }catch (Exception e){
            throw new RuntimeException("Error getting mapper instance. Cause: " + e, e);
        }
    }


    /**
     * 若符合规则，添加映射
     * @param type
     * @param <T>
     */
    public <T> void addMapper(Class<T> type){
        if (type.isInterface()){
            // 如果是接口，才可以注册
            if (hasMapper(type)){
                // 如果重复注册，抛出异常
                throw new RuntimeException("type "+ type + "is already exist in MapperRegistry");
            }

            // 向MapperRegistry注册当前类型
            knowMappers.put(type,new MapperProxyFactory<>(type));
        }
    }


    /**
     * 通过包扫描批量注册mapper代理工厂
     * @param packageName
     */
    public void addMappers(String packageName){
        // 利用hutool工具包中的ClassScanner，扫描包路径下所有的class文件
        Set<Class<?>> mappersClasses = ClassScanner.scanPackage(packageName);
        for (Class<?> mapperClass : mappersClasses) {
            addMapper(mapperClass);
        }
    }


    public  <T> boolean hasMapper(Class<T> type) {
        return knowMappers.containsKey(type);
    }


}
